package org.hackystat.devcathlon.engine.event;

import java.io.Serializable;
import javax.xml.datatype.XMLGregorianCalendar;
import org.hackystat.devcathlon.configuration.ConfigurationException;
import org.hackystat.devcathlon.configuration.ConfigurationManager;
import org.hackystat.devcathlon.configuration.jaxb.DevcathlonConfiguration;
import org.hackystat.devcathlon.configuration.jaxb.EventConfiguration;
import org.hackystat.devcathlon.engine.match.Match;
import org.hackystat.devcathlon.engine.team.Team;

/**
 * Provides the abstract class that is extended by all event instances. All Events must be able to
 * produce an EventScore given a match and a team. To get the actual points that an EventScore is
 * worth, the Event can access information from the DevcathlonConfiguration.
 * 
 * @author Philip Johnson
 */
public abstract class Event implements Serializable {
  /** Support serialization. */
  private static final long serialVersionUID = 1L;
  /** Primary key reference id. */
  private Long id;
  /** Holds a pointer to the DevcathlonConfiguration instance for this system. */
  protected DevcathlonConfiguration config;
  /** The unique name for this event. */
  protected String name;
  /** The unique name for this event. */
  protected String description;
  /** The wakeup interval (if any). */
  protected int wakeupInterval;
  /** The base score weight. */
  protected int baseScoreWeight;
  /** The event configuration for this event. */
  protected EventConfiguration eventConfig;
  /** The configuration manager. */
  protected ConfigurationManager configManager;

  /**
   * Default Event constructor.
   */
  public Event() {
    // empty constructor.
  }
  
  /**
   * Instantiates an Event, initializing a pointer to the configuration and the logger.
   * 
   * @param eventName The name of the event.
   * @param description The description of the event.
   * @throws EventException If problems occur getting the configuration.
   */
  public Event(String eventName, String description) throws EventException {
    this.setName(eventName);
    this.description = description;
    this.configManager = ConfigurationManager.getInstance();

    try {
      this.config = configManager.getConfiguration();
      this.wakeupInterval = configManager.getWakeupInterval(this.name);
      this.baseScoreWeight = configManager.getBaseScoreWeight(this.name);
      this.eventConfig = configManager.getConfiguration(this.name);
    }
    catch (ConfigurationException e) {
      throw new EventException("Error processing configuration for " + eventName, e);
    }
    // this.logger = HackystatLogger.getLogger("org.hackystat.devcathlon", "devcathlon");
  }
  
  /**
   * Returns the unique id. 
   * @return the id
   */
  public Long getId() {
    return id;
  }

  /**
   * Sets the unique id.
   * @param id the id to set
   */
  public void setId(Long id) {
    this.id = id;
  }

  /**
   * Returns the base score weight for this event.
   * 
   * @return the baseScoreWeight
   */
  public int getBaseScoreWeight() {
    return baseScoreWeight;
  }

  /**
   * Sets the base score weight for this event.
   * 
   * @param baseScoreWeight the baseScoreWeight to set
   */
  public void setBaseScoreWeight(int baseScoreWeight) {
    this.baseScoreWeight = baseScoreWeight;
  }

  /**
   * Returns an EventScore instance indicating the score achieved by the given team for the given
   * Match on this particular Event.
   * 
   * @param match The match of interest.
   * @param team The team whose score is to be computed.
   * @param timestamp The time at which this event is being run.
   * @return The EventScore.
   * @throws EventException If problems occur while processing this event.
   * @throws Exception If there is an error.
   */
  public abstract EventScore computeScore(Match match, Team team, XMLGregorianCalendar timestamp)
      throws EventException, Exception;

  /**
   * Returns the number of points awarded to this Team for the passed EventScore. The Event instance
   * can consult the DevcathlonConfiguration to determine the points.
   * 
   * @param score The EventScore whose points are to be determined.
   * @return An integer indicating the points for this EventScore.
   */
  public abstract int computePoints(EventScore score);

  /**
   * Returns the name of this Event.
   * 
   * @return The name of this event.
   */
  public String getName() {
    return this.name;
  }
  
  /**
   * Sets teh name of this Event.
   * 
   * @param name The event name.
   */
  private void setName(String name) {
    this.name = name;
  }

  /**
   * Returns a description of this Event.
   * 
   * @return The description of this event.
   */
  public String getDescription() {
    return this.description;
  }
  
  /**
   * Sets a description of this Event.
   * 
   * @param description The description of this event.
   */
  private void setDescription(String description) {
    this.description = description;
  }

  /**
   * Returns the WakeupInterval of the event.
   * 
   * @return The WakeupInterval of this event.
   */
  public int getWakeupInterval() {
    return this.wakeupInterval;
  }
  
  /**
   * Sets the WakeupInterval of this Event.
   * 
   * @param wakeupInterval The wakeup interval of this event.
   */
  private void setWakeupInterval(int wakeupInterval) {
    this.wakeupInterval = wakeupInterval;
  }

  /**
   * Override equals in order to compare Event objects.
   * 
   * @param obj The Event that will be compared.
   * @return If Event object is null or wrong type returns false. Else if events are the same
   * returns true.
   */
  public boolean equals(Object obj) {
    if (!(obj instanceof Event)) {
      return false;
    }
    Event c = (Event) obj;
    return c.name.equals(this.name);
  }

  /**
   * Override hashCode.
   * 
   * @return Arbitrary constant since hashCode isn't used.
   */
  @Override
  public int hashCode() {
        int hash = 1;
    hash = hash * 31 + name.hashCode();
    hash = hash * 31
                + (description == null ? 0 : description.hashCode());
    return hash;
//    assert false : "hashCode not designed";
//    return 1; // any arbitrary constant will do
  }
}
